package body AES
is
  function Shift_Left(value: word; amount: Natural) return word
  is
    result: word;
  begin
    if amount >= 32 then
      result := 0;
    else
      result := value * (2 ** amount);   
    end if;
    return result;
  end Shift_Left;

  function Shift_Right(value: word; amount: Natural) return word
  is
    result: word;
  begin
    if amount >= 32 then
      result := 0;
    else
      result := value / (2 ** amount);
    end if;
    return result;
  end Shift_Right;

  function CombineWord(b0, b1, b2, b3: byte) return word
  is
  begin
    return Shift_Left(word(b0),24) xor
           Shift_Left(word(b1),16) xor
           Shift_Left(word(b2), 8) xor
                      word(b3);
  end CombineWord;

  procedure SplitWord(w: in word; b0, b1, b2, b3: out byte)
  is
  begin
    b0 := byte(Shift_Right(w,24)           );
    b1 := byte(Shift_Right(w,16) and 16#ff#);
    b2 := byte(Shift_Right(w, 8) and 16#ff#);
    b3 := byte(            w     and 16#ff#);
  end SplitWord;

  procedure KeySetupEnc(rk: out key_schedule; cipherKey: in key; Nk: in Integer; Nr: out Integer)
  is
    i, p: Integer;
	  temp: word;
	  W: key_schedule := key_schedule'(others => 0);
  begin
    Nr := Nk + 6;
    rk := key_schedule'(others => 0);
    i := 0;

    W(0) := CombineWord(cipherKey( 0), cipherKey( 1), cipherKey( 2), cipherKey( 3));
    W(1) := CombineWord(cipherKey( 4), cipherKey( 5), cipherKey( 6), cipherKey( 7));
    W(2) := CombineWord(cipherKey( 8), cipherKey( 9), cipherKey(10), cipherKey(11));
    W(3) := CombineWord(cipherKey(12), cipherKey(13), cipherKey(14), cipherKey(15));

    if (Nk = 4) then
      p := 4;
      loop
        temp := W(p-1);
        W(p) := W(p-4) xor
                (Te4(byte(Shift_Right(temp,16) and 16#ff#)) and 16#ff000000#) xor
                (Te4(byte(Shift_Right(temp, 8) and 16#ff#)) and 16#00ff0000#) xor
                (Te4(byte(            temp     and 16#ff#)) and 16#0000ff00#) xor
                (Te4(byte(Shift_Right(temp,24)           )) and 16#000000ff#) xor
                rcon(i);
        W(p+1) := W(p-3) xor W(p);
        W(p+2) := W(p-2) xor W(p+1);
        W(p+3) := W(p-1) xor W(p+2);
        i := i + 1;
        exit when i >= 10;
        p := p + 4; -- p = 4*(i+1)
      end loop;
      rk := W;
    end if;

    W(4) := CombineWord(cipherKey(16), cipherKey(17), cipherKey(18), cipherKey(19));
    W(5) := CombineWord(cipherKey(20), cipherKey(21), cipherKey(22), cipherKey(23));

    if (Nk = 6) then
      p := 6;
      loop
        temp := W(p-1);
        W(p) := W(p-6) xor
                (Te4(byte(Shift_Right(temp,16) and 16#ff#)) and 16#ff000000#) xor
                (Te4(byte(Shift_Right(temp, 8) and 16#ff#)) and 16#00ff0000#) xor
                (Te4(byte(            temp     and 16#ff#)) and 16#0000ff00#) xor
                (Te4(byte(Shift_Right(temp,24)           )) and 16#000000ff#) xor
                rcon(i);
        W(p+1) := W(p-5) xor W(p);
        W(p+2) := W(p-4) xor W(p+1);
        W(p+3) := W(p-3) xor W(p+2);
        i := i + 1;
        exit when i >= 8;
        W(p+4) := W(p-2) xor W(p+3);
        W(p+5) := W(p-1) xor W(p+4);
        p := p + 6; -- p = 6*(i+1)
      end loop;
      rk := W;
    end if;

    W(6) := CombineWord(cipherKey(24), cipherKey(25), cipherKey(26), cipherKey(27));
    W(7) := CombineWord(cipherKey(28), cipherKey(29), cipherKey(30), cipherKey(31));

    if (Nk = 8) then
      p := 8;
      loop
        temp := W(p-1);
        W(p) := W(p-8) xor
                (Te4(byte(Shift_Right(temp,16) and 16#ff#)) and 16#ff000000#) xor
                (Te4(byte(Shift_Right(temp, 8) and 16#ff#)) and 16#00ff0000#) xor
                (Te4(byte(            temp     and 16#ff#)) and 16#0000ff00#) xor
                (Te4(byte(Shift_Right(temp,24)           )) and 16#000000ff#) xor
                rcon(i);
        W(p+1) := W(p-7) xor W(p);
        W(p+2) := W(p-6) xor W(p+1);
        W(p+3) := W(p-5) xor W(p+2);
        i := i + 1;
        exit when i >= 7;
        temp := W(p+3);
        W(p+4) := W(p-4) xor
                  (Te4(byte(Shift_Right(temp,24)           )) and 16#ff000000#) xor
                  (Te4(byte(Shift_Right(temp,16) and 16#ff#)) and 16#00ff0000#) xor
                  (Te4(byte(Shift_Right(temp, 8) and 16#ff#)) and 16#0000ff00#) xor
                  (Te4(byte(            temp     and 16#ff#)) and 16#000000ff#);
        W(p+5) := W(p-3) xor W(p+4);
        W(p+6) := W(p-2) xor W(p+5);
        W(p+7) := W(p-1) xor W(p+6);
        p := p + 8; -- p = 8*(i+1)
      end loop;
      rk := W;
    end if;
  end KeySetupEnc;

  procedure KeySetupDec(rk: out key_schedule; cipherKey: in key; Nk: in Integer; Nr: out Integer)
  is
    i, j, p: Integer;
	  temp: word;
  begin
    KeySetupEnc(rk, cipherKey, Nk, Nr);

    i := 0; j := 4*Nr;
    while (i < j) loop
      temp := rk(i  ); rk(i  ) := rk(j  ); rk(j  ) := temp;
		  temp := rk(i+1); rk(i+1) := rk(j+1); rk(j+1) := temp;
		  temp := rk(i+2); rk(i+2) := rk(j+2); rk(j+2) := temp;
		  temp := rk(i+3); rk(i+3) := rk(j+3); rk(j+3) := temp;
		  i := i + 4; j := j - 4;
    end loop;

    p := 0;
    for k in Integer range 1 .. Nr-1 loop
      p := p + 4;
      rk(p  ) := Td0(byte(Te4(byte(Shift_Right(rk(p  ),24)           )) and 16#ff#)) xor
                 Td1(byte(Te4(byte(Shift_Right(rk(p  ),16) and 16#ff#)) and 16#ff#)) xor
                 Td2(byte(Te4(byte(Shift_Right(rk(p  ), 8) and 16#ff#)) and 16#ff#)) xor
                 Td3(byte(Te4(byte(            rk(p  )     and 16#ff#)) and 16#ff#));
      rk(p+1) := Td0(byte(Te4(byte(Shift_Right(rk(p+1),24)           )) and 16#ff#)) xor
                 Td1(byte(Te4(byte(Shift_Right(rk(p+1),16) and 16#ff#)) and 16#ff#)) xor
                 Td2(byte(Te4(byte(Shift_Right(rk(p+1), 8) and 16#ff#)) and 16#ff#)) xor
                 Td3(byte(Te4(byte(            rk(p+1)     and 16#ff#)) and 16#ff#));
      rk(p+2) := Td0(byte(Te4(byte(Shift_Right(rk(p+2),24)           )) and 16#ff#)) xor
                 Td1(byte(Te4(byte(Shift_Right(rk(p+2),16) and 16#ff#)) and 16#ff#)) xor
                 Td2(byte(Te4(byte(Shift_Right(rk(p+2), 8) and 16#ff#)) and 16#ff#)) xor
                 Td3(byte(Te4(byte(            rk(p+2)     and 16#ff#)) and 16#ff#));
      rk(p+3) := Td0(byte(Te4(byte(Shift_Right(rk(p+3),24)           )) and 16#ff#)) xor
                 Td1(byte(Te4(byte(Shift_Right(rk(p+3),16) and 16#ff#)) and 16#ff#)) xor
                 Td2(byte(Te4(byte(Shift_Right(rk(p+3), 8) and 16#ff#)) and 16#ff#)) xor
                 Td3(byte(Te4(byte(            rk(p+3)     and 16#ff#)) and 16#ff#));
    end loop;
  end KeySetupDec;

  procedure aesKeySetupEnc(rk: out key_schedule; cipherKey: in key; keyBits: in Integer; Nr: out Integer)
  is
  begin
    KeySetupEnc(rk, cipherKey, keyBits/32, Nr);
  end aesKeySetupEnc;

  procedure aesKeySetupDec(rk: out key_schedule; cipherKey: in key; keyBits: in Integer; Nr: out Integer)
  is
  begin
    KeySetupDec(rk, cipherKey, keyBits/32, Nr);
  end aesKeySetupDec;

  procedure aesEncrypt(rk: in key_schedule; Nr: in Integer; pt: in block; ct: out block)
  is
    s0, s1, s2, s3, t0, t1, t2, t3: word;
    b0, b1, b2, b3: byte;
  begin
    -- map byte array block to cipher state
    -- and add initial round key:
    s0 := CombineWord(pt( 0), pt( 1), pt( 2), pt( 3)) xor rk(0); 
    s1 := CombineWord(pt( 4), pt( 5), pt( 6), pt( 7)) xor rk(1);
    s2 := CombineWord(pt( 8), pt( 9), pt(10), pt(11)) xor rk(2);
    s3 := CombineWord(pt(12), pt(13), pt(14), pt(15)) xor rk(3);
    
    -- round 1:
    t0 := Te0(byte(Shift_Right(s0, 24))) xor Te1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Te3(byte(s3 and 16#ff#)) xor rk( 4);
   	t1 := Te0(byte(Shift_Right(s1, 24))) xor Te1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Te3(byte(s0 and 16#ff#)) xor rk( 5);
   	t2 := Te0(byte(Shift_Right(s2, 24))) xor Te1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Te3(byte(s1 and 16#ff#)) xor rk( 6);
   	t3 := Te0(byte(Shift_Right(s3, 24))) xor Te1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Te3(byte(s2 and 16#ff#)) xor rk( 7);

   	-- round 2:
    s0 := Te0(byte(Shift_Right(t0, 24))) xor Te1(byte(Shift_Right(t1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t2, 8) and 16#ff#)) xor Te3(byte(t3 and 16#ff#)) xor rk( 8);
   	s1 := Te0(byte(Shift_Right(t1, 24))) xor Te1(byte(Shift_Right(t2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t3, 8) and 16#ff#)) xor Te3(byte(t0 and 16#ff#)) xor rk( 9);
   	s2 := Te0(byte(Shift_Right(t2, 24))) xor Te1(byte(Shift_Right(t3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t0, 8) and 16#ff#)) xor Te3(byte(t1 and 16#ff#)) xor rk(10);
   	s3 := Te0(byte(Shift_Right(t3, 24))) xor Te1(byte(Shift_Right(t0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t1, 8) and 16#ff#)) xor Te3(byte(t2 and 16#ff#)) xor rk(11);

    -- round 3:
    t0 := Te0(byte(Shift_Right(s0, 24))) xor Te1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Te3(byte(s3 and 16#ff#)) xor rk(12);
   	t1 := Te0(byte(Shift_Right(s1, 24))) xor Te1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Te3(byte(s0 and 16#ff#)) xor rk(13);
   	t2 := Te0(byte(Shift_Right(s2, 24))) xor Te1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Te3(byte(s1 and 16#ff#)) xor rk(14);
   	t3 := Te0(byte(Shift_Right(s3, 24))) xor Te1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Te3(byte(s2 and 16#ff#)) xor rk(15);

   	-- round 4:
    s0 := Te0(byte(Shift_Right(t0, 24))) xor Te1(byte(Shift_Right(t1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t2, 8) and 16#ff#)) xor Te3(byte(t3 and 16#ff#)) xor rk(16);
   	s1 := Te0(byte(Shift_Right(t1, 24))) xor Te1(byte(Shift_Right(t2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t3, 8) and 16#ff#)) xor Te3(byte(t0 and 16#ff#)) xor rk(17);
   	s2 := Te0(byte(Shift_Right(t2, 24))) xor Te1(byte(Shift_Right(t3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t0, 8) and 16#ff#)) xor Te3(byte(t1 and 16#ff#)) xor rk(18);
   	s3 := Te0(byte(Shift_Right(t3, 24))) xor Te1(byte(Shift_Right(t0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t1, 8) and 16#ff#)) xor Te3(byte(t2 and 16#ff#)) xor rk(19);

    -- round 5:
    t0 := Te0(byte(Shift_Right(s0, 24))) xor Te1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Te3(byte(s3 and 16#ff#)) xor rk(20);
   	t1 := Te0(byte(Shift_Right(s1, 24))) xor Te1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Te3(byte(s0 and 16#ff#)) xor rk(21);
   	t2 := Te0(byte(Shift_Right(s2, 24))) xor Te1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Te3(byte(s1 and 16#ff#)) xor rk(22);
   	t3 := Te0(byte(Shift_Right(s3, 24))) xor Te1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Te3(byte(s2 and 16#ff#)) xor rk(23);

   	-- round 6:
    s0 := Te0(byte(Shift_Right(t0, 24))) xor Te1(byte(Shift_Right(t1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t2, 8) and 16#ff#)) xor Te3(byte(t3 and 16#ff#)) xor rk(24);
   	s1 := Te0(byte(Shift_Right(t1, 24))) xor Te1(byte(Shift_Right(t2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t3, 8) and 16#ff#)) xor Te3(byte(t0 and 16#ff#)) xor rk(25);
   	s2 := Te0(byte(Shift_Right(t2, 24))) xor Te1(byte(Shift_Right(t3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t0, 8) and 16#ff#)) xor Te3(byte(t1 and 16#ff#)) xor rk(26);
   	s3 := Te0(byte(Shift_Right(t3, 24))) xor Te1(byte(Shift_Right(t0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t1, 8) and 16#ff#)) xor Te3(byte(t2 and 16#ff#)) xor rk(27);

    -- round 7:
    t0 := Te0(byte(Shift_Right(s0, 24))) xor Te1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Te3(byte(s3 and 16#ff#)) xor rk(28);
   	t1 := Te0(byte(Shift_Right(s1, 24))) xor Te1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Te3(byte(s0 and 16#ff#)) xor rk(29);
   	t2 := Te0(byte(Shift_Right(s2, 24))) xor Te1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Te3(byte(s1 and 16#ff#)) xor rk(30);
   	t3 := Te0(byte(Shift_Right(s3, 24))) xor Te1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Te3(byte(s2 and 16#ff#)) xor rk(31);

   	-- round 8:
    s0 := Te0(byte(Shift_Right(t0, 24))) xor Te1(byte(Shift_Right(t1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t2, 8) and 16#ff#)) xor Te3(byte(t3 and 16#ff#)) xor rk(32);
   	s1 := Te0(byte(Shift_Right(t1, 24))) xor Te1(byte(Shift_Right(t2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t3, 8) and 16#ff#)) xor Te3(byte(t0 and 16#ff#)) xor rk(33);
   	s2 := Te0(byte(Shift_Right(t2, 24))) xor Te1(byte(Shift_Right(t3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t0, 8) and 16#ff#)) xor Te3(byte(t1 and 16#ff#)) xor rk(34);
   	s3 := Te0(byte(Shift_Right(t3, 24))) xor Te1(byte(Shift_Right(t0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t1, 8) and 16#ff#)) xor Te3(byte(t2 and 16#ff#)) xor rk(35);

    -- round 9:
    t0 := Te0(byte(Shift_Right(s0, 24))) xor Te1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Te3(byte(s3 and 16#ff#)) xor rk(36);
   	t1 := Te0(byte(Shift_Right(s1, 24))) xor Te1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Te3(byte(s0 and 16#ff#)) xor rk(37);
   	t2 := Te0(byte(Shift_Right(s2, 24))) xor Te1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Te3(byte(s1 and 16#ff#)) xor rk(38);
   	t3 := Te0(byte(Shift_Right(s3, 24))) xor Te1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Te3(byte(s2 and 16#ff#)) xor rk(39);

    if (Nr > 10) then
     	-- round 10:
      s0 := Te0(byte(Shift_Right(t0, 24))) xor Te1(byte(Shift_Right(t1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t2, 8) and 16#ff#)) xor Te3(byte(t3 and 16#ff#)) xor rk(40);
   	  s1 := Te0(byte(Shift_Right(t1, 24))) xor Te1(byte(Shift_Right(t2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t3, 8) and 16#ff#)) xor Te3(byte(t0 and 16#ff#)) xor rk(41);
   	  s2 := Te0(byte(Shift_Right(t2, 24))) xor Te1(byte(Shift_Right(t3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t0, 8) and 16#ff#)) xor Te3(byte(t1 and 16#ff#)) xor rk(42);
   	  s3 := Te0(byte(Shift_Right(t3, 24))) xor Te1(byte(Shift_Right(t0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t1, 8) and 16#ff#)) xor Te3(byte(t2 and 16#ff#)) xor rk(43);

      -- round 11:
      t0 := Te0(byte(Shift_Right(s0, 24))) xor Te1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Te3(byte(s3 and 16#ff#)) xor rk(44);
   	  t1 := Te0(byte(Shift_Right(s1, 24))) xor Te1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Te3(byte(s0 and 16#ff#)) xor rk(45);
   	  t2 := Te0(byte(Shift_Right(s2, 24))) xor Te1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Te3(byte(s1 and 16#ff#)) xor rk(46);
   	  t3 := Te0(byte(Shift_Right(s3, 24))) xor Te1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Te3(byte(s2 and 16#ff#)) xor rk(47);

      if (Nr > 12) then
       	-- round 12:
        s0 := Te0(byte(Shift_Right(t0, 24))) xor Te1(byte(Shift_Right(t1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t2, 8) and 16#ff#)) xor Te3(byte(t3 and 16#ff#)) xor rk(48);
   	    s1 := Te0(byte(Shift_Right(t1, 24))) xor Te1(byte(Shift_Right(t2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t3, 8) and 16#ff#)) xor Te3(byte(t0 and 16#ff#)) xor rk(49);
   	    s2 := Te0(byte(Shift_Right(t2, 24))) xor Te1(byte(Shift_Right(t3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t0, 8) and 16#ff#)) xor Te3(byte(t1 and 16#ff#)) xor rk(50);
   	    s3 := Te0(byte(Shift_Right(t3, 24))) xor Te1(byte(Shift_Right(t0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(t1, 8) and 16#ff#)) xor Te3(byte(t2 and 16#ff#)) xor rk(51);

        -- round 13:
        t0 := Te0(byte(Shift_Right(s0, 24))) xor Te1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Te3(byte(s3 and 16#ff#)) xor rk(52);
   	    t1 := Te0(byte(Shift_Right(s1, 24))) xor Te1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Te3(byte(s0 and 16#ff#)) xor rk(53);
   	    t2 := Te0(byte(Shift_Right(s2, 24))) xor Te1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Te3(byte(s1 and 16#ff#)) xor rk(54);
   	    t3 := Te0(byte(Shift_Right(s3, 24))) xor Te1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Te2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Te3(byte(s2 and 16#ff#)) xor rk(55);
      end if;
    end if;

    -- last round:
    s0 := (Te4(byte(Shift_Right(t0,24)           )) and 16#ff000000#) xor
          (Te4(byte(Shift_Right(t1,16) and 16#ff#)) and 16#00ff0000#) xor
          (Te4(byte(Shift_Right(t2, 8) and 16#ff#)) and 16#0000ff00#) xor
          (Te4(byte(            t3     and 16#ff#)) and 16#000000ff#) xor
          rk(4*Nr  );

    s1 := (Te4(byte(Shift_Right(t1,24)           )) and 16#ff000000#) xor
          (Te4(byte(Shift_Right(t2,16) and 16#ff#)) and 16#00ff0000#) xor
          (Te4(byte(Shift_Right(t3, 8) and 16#ff#)) and 16#0000ff00#) xor
          (Te4(byte(            t0     and 16#ff#)) and 16#000000ff#) xor
          rk(4*Nr+1);

    s2 := (Te4(byte(Shift_Right(t2,24)           )) and 16#ff000000#) xor
          (Te4(byte(Shift_Right(t3,16) and 16#ff#)) and 16#00ff0000#) xor
          (Te4(byte(Shift_Right(t0, 8) and 16#ff#)) and 16#0000ff00#) xor
          (Te4(byte(            t1     and 16#ff#)) and 16#000000ff#) xor
          rk(4*Nr+2);

    s3 := (Te4(byte(Shift_Right(t3,24)           )) and 16#ff000000#) xor
          (Te4(byte(Shift_Right(t0,16) and 16#ff#)) and 16#00ff0000#) xor
          (Te4(byte(Shift_Right(t1, 8) and 16#ff#)) and 16#0000ff00#) xor
          (Te4(byte(            t2     and 16#ff#)) and 16#000000ff#) xor
          rk(4*Nr+3);

    -- map cipher state to byte array block:
    ct := block'(others => 0);
    SplitWord(s0, b0, b1, b2, b3);
    ct( 0) := b0; ct( 1) := b1; ct( 2) := b2; ct( 3) := b3;
    SplitWord(s1, b0, b1, b2, b3);
    ct( 4) := b0; ct( 5) := b1; ct( 6) := b2; ct( 7) := b3;
    SplitWord(s2, b0, b1, b2, b3);
    ct( 8) := b0; ct( 9) := b1; ct(10) := b2; ct(11) := b3;
    SplitWord(s3, b0, b1, b2, b3);
    ct(12) := b0; ct(13) := b1; ct(14) := b2; ct(15) := b3;
  end aesEncrypt;

  procedure aesDecrypt(rk: in key_schedule; Nr: in Integer; ct: in block; pt: out block)
  is
    s0, s1, s2, s3, t0, t1, t2, t3: word;
    b0, b1, b2, b3: byte;
  begin
    -- map byte array block to cipher state
    -- and add initial round key:
    s0 := CombineWord(ct( 0), ct( 1), ct( 2), ct( 3)) xor rk(0);
    s1 := CombineWord(ct( 4), ct( 5), ct( 6), ct( 7)) xor rk(1);
    s2 := CombineWord(ct( 8), ct( 9), ct(10), ct(11)) xor rk(2);
    s3 := CombineWord(ct(12), ct(13), ct(14), ct(15)) xor rk(3);

    -- round 1:
    t0 := Td0(byte(Shift_Right(s0, 24))) xor Td1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Td3(byte(s1 and 16#ff#)) xor rk( 4);
   	t1 := Td0(byte(Shift_Right(s1, 24))) xor Td1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Td3(byte(s2 and 16#ff#)) xor rk( 5);
   	t2 := Td0(byte(Shift_Right(s2, 24))) xor Td1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Td3(byte(s3 and 16#ff#)) xor rk( 6);
   	t3 := Td0(byte(Shift_Right(s3, 24))) xor Td1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Td3(byte(s0 and 16#ff#)) xor rk( 7);

   	-- round 2:
    s0 := Td0(byte(Shift_Right(t0, 24))) xor Td1(byte(Shift_Right(t3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t2, 8) and 16#ff#)) xor Td3(byte(t1 and 16#ff#)) xor rk( 8);
   	s1 := Td0(byte(Shift_Right(t1, 24))) xor Td1(byte(Shift_Right(t0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t3, 8) and 16#ff#)) xor Td3(byte(t2 and 16#ff#)) xor rk( 9);
   	s2 := Td0(byte(Shift_Right(t2, 24))) xor Td1(byte(Shift_Right(t1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t0, 8) and 16#ff#)) xor Td3(byte(t3 and 16#ff#)) xor rk(10);
   	s3 := Td0(byte(Shift_Right(t3, 24))) xor Td1(byte(Shift_Right(t2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t1, 8) and 16#ff#)) xor Td3(byte(t0 and 16#ff#)) xor rk(11);

    -- round 3:
    t0 := Td0(byte(Shift_Right(s0, 24))) xor Td1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Td3(byte(s1 and 16#ff#)) xor rk(12);
   	t1 := Td0(byte(Shift_Right(s1, 24))) xor Td1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Td3(byte(s2 and 16#ff#)) xor rk(13);
   	t2 := Td0(byte(Shift_Right(s2, 24))) xor Td1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Td3(byte(s3 and 16#ff#)) xor rk(14);
   	t3 := Td0(byte(Shift_Right(s3, 24))) xor Td1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Td3(byte(s0 and 16#ff#)) xor rk(15);

   	-- round 4:
    s0 := Td0(byte(Shift_Right(t0, 24))) xor Td1(byte(Shift_Right(t3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t2, 8) and 16#ff#)) xor Td3(byte(t1 and 16#ff#)) xor rk(16);
   	s1 := Td0(byte(Shift_Right(t1, 24))) xor Td1(byte(Shift_Right(t0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t3, 8) and 16#ff#)) xor Td3(byte(t2 and 16#ff#)) xor rk(17);
   	s2 := Td0(byte(Shift_Right(t2, 24))) xor Td1(byte(Shift_Right(t1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t0, 8) and 16#ff#)) xor Td3(byte(t3 and 16#ff#)) xor rk(18);
   	s3 := Td0(byte(Shift_Right(t3, 24))) xor Td1(byte(Shift_Right(t2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t1, 8) and 16#ff#)) xor Td3(byte(t0 and 16#ff#)) xor rk(19);

    -- round 5:
    t0 := Td0(byte(Shift_Right(s0, 24))) xor Td1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Td3(byte(s1 and 16#ff#)) xor rk(20);
   	t1 := Td0(byte(Shift_Right(s1, 24))) xor Td1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Td3(byte(s2 and 16#ff#)) xor rk(21);
   	t2 := Td0(byte(Shift_Right(s2, 24))) xor Td1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Td3(byte(s3 and 16#ff#)) xor rk(22);
   	t3 := Td0(byte(Shift_Right(s3, 24))) xor Td1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Td3(byte(s0 and 16#ff#)) xor rk(23);

   	-- round 6:
    s0 := Td0(byte(Shift_Right(t0, 24))) xor Td1(byte(Shift_Right(t3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t2, 8) and 16#ff#)) xor Td3(byte(t1 and 16#ff#)) xor rk(24);
   	s1 := Td0(byte(Shift_Right(t1, 24))) xor Td1(byte(Shift_Right(t0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t3, 8) and 16#ff#)) xor Td3(byte(t2 and 16#ff#)) xor rk(25);
   	s2 := Td0(byte(Shift_Right(t2, 24))) xor Td1(byte(Shift_Right(t1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t0, 8) and 16#ff#)) xor Td3(byte(t3 and 16#ff#)) xor rk(26);
   	s3 := Td0(byte(Shift_Right(t3, 24))) xor Td1(byte(Shift_Right(t2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t1, 8) and 16#ff#)) xor Td3(byte(t0 and 16#ff#)) xor rk(27);

    -- round 7:
    t0 := Td0(byte(Shift_Right(s0, 24))) xor Td1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Td3(byte(s1 and 16#ff#)) xor rk(28);
   	t1 := Td0(byte(Shift_Right(s1, 24))) xor Td1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Td3(byte(s2 and 16#ff#)) xor rk(29);
   	t2 := Td0(byte(Shift_Right(s2, 24))) xor Td1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Td3(byte(s3 and 16#ff#)) xor rk(30);
   	t3 := Td0(byte(Shift_Right(s3, 24))) xor Td1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Td3(byte(s0 and 16#ff#)) xor rk(31);

   	-- round 8:
    s0 := Td0(byte(Shift_Right(t0, 24))) xor Td1(byte(Shift_Right(t3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t2, 8) and 16#ff#)) xor Td3(byte(t1 and 16#ff#)) xor rk(32);
   	s1 := Td0(byte(Shift_Right(t1, 24))) xor Td1(byte(Shift_Right(t0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t3, 8) and 16#ff#)) xor Td3(byte(t2 and 16#ff#)) xor rk(33);
   	s2 := Td0(byte(Shift_Right(t2, 24))) xor Td1(byte(Shift_Right(t1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t0, 8) and 16#ff#)) xor Td3(byte(t3 and 16#ff#)) xor rk(34);
   	s3 := Td0(byte(Shift_Right(t3, 24))) xor Td1(byte(Shift_Right(t2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t1, 8) and 16#ff#)) xor Td3(byte(t0 and 16#ff#)) xor rk(35);

    -- round 9:
    t0 := Td0(byte(Shift_Right(s0, 24))) xor Td1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Td3(byte(s1 and 16#ff#)) xor rk(36);
   	t1 := Td0(byte(Shift_Right(s1, 24))) xor Td1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Td3(byte(s2 and 16#ff#)) xor rk(37);
   	t2 := Td0(byte(Shift_Right(s2, 24))) xor Td1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Td3(byte(s3 and 16#ff#)) xor rk(38);
   	t3 := Td0(byte(Shift_Right(s3, 24))) xor Td1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Td3(byte(s0 and 16#ff#)) xor rk(39);

    if (Nr > 10) then
     	-- round 10:
      s0 := Td0(byte(Shift_Right(t0, 24))) xor Td1(byte(Shift_Right(t3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t2, 8) and 16#ff#)) xor Td3(byte(t1 and 16#ff#)) xor rk(40);
     	s1 := Td0(byte(Shift_Right(t1, 24))) xor Td1(byte(Shift_Right(t0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t3, 8) and 16#ff#)) xor Td3(byte(t2 and 16#ff#)) xor rk(41);
   	  s2 := Td0(byte(Shift_Right(t2, 24))) xor Td1(byte(Shift_Right(t1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t0, 8) and 16#ff#)) xor Td3(byte(t3 and 16#ff#)) xor rk(42);
     	s3 := Td0(byte(Shift_Right(t3, 24))) xor Td1(byte(Shift_Right(t2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t1, 8) and 16#ff#)) xor Td3(byte(t0 and 16#ff#)) xor rk(43);

      -- round 11:
      t0 := Td0(byte(Shift_Right(s0, 24))) xor Td1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Td3(byte(s1 and 16#ff#)) xor rk(44);
     	t1 := Td0(byte(Shift_Right(s1, 24))) xor Td1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Td3(byte(s2 and 16#ff#)) xor rk(45);
     	t2 := Td0(byte(Shift_Right(s2, 24))) xor Td1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Td3(byte(s3 and 16#ff#)) xor rk(46);
   	  t3 := Td0(byte(Shift_Right(s3, 24))) xor Td1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Td3(byte(s0 and 16#ff#)) xor rk(47);

      if (Nr > 12) then
       	-- round 12:
        s0 := Td0(byte(Shift_Right(t0, 24))) xor Td1(byte(Shift_Right(t3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t2, 8) and 16#ff#)) xor Td3(byte(t1 and 16#ff#)) xor rk(48);
       	s1 := Td0(byte(Shift_Right(t1, 24))) xor Td1(byte(Shift_Right(t0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t3, 8) and 16#ff#)) xor Td3(byte(t2 and 16#ff#)) xor rk(49);
   	    s2 := Td0(byte(Shift_Right(t2, 24))) xor Td1(byte(Shift_Right(t1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t0, 8) and 16#ff#)) xor Td3(byte(t3 and 16#ff#)) xor rk(50);
       	s3 := Td0(byte(Shift_Right(t3, 24))) xor Td1(byte(Shift_Right(t2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(t1, 8) and 16#ff#)) xor Td3(byte(t0 and 16#ff#)) xor rk(51);

        -- round 13:
        t0 := Td0(byte(Shift_Right(s0, 24))) xor Td1(byte(Shift_Right(s3, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s2, 8) and 16#ff#)) xor Td3(byte(s1 and 16#ff#)) xor rk(52);
       	t1 := Td0(byte(Shift_Right(s1, 24))) xor Td1(byte(Shift_Right(s0, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s3, 8) and 16#ff#)) xor Td3(byte(s2 and 16#ff#)) xor rk(53);
     	  t2 := Td0(byte(Shift_Right(s2, 24))) xor Td1(byte(Shift_Right(s1, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s0, 8) and 16#ff#)) xor Td3(byte(s3 and 16#ff#)) xor rk(54);
     	  t3 := Td0(byte(Shift_Right(s3, 24))) xor Td1(byte(Shift_Right(s2, 16) and 16#ff#)) xor Td2(byte(Shift_Right(s1, 8) and 16#ff#)) xor Td3(byte(s0 and 16#ff#)) xor rk(55);
      end if;
    end if;

    -- last round:
    s0 := (Td4(byte(Shift_Right(t0,24)           )) and 16#ff000000#) xor
          (Td4(byte(Shift_Right(t3,16) and 16#ff#)) and 16#00ff0000#) xor
          (Td4(byte(Shift_Right(t2, 8) and 16#ff#)) and 16#0000ff00#) xor
          (Td4(byte(            t1     and 16#ff#)) and 16#000000ff#) xor
          rk(4*Nr  );

    s1 := (Td4(byte(Shift_Right(t1,24)           )) and 16#ff000000#) xor
          (Td4(byte(Shift_Right(t0,16) and 16#ff#)) and 16#00ff0000#) xor
          (Td4(byte(Shift_Right(t3, 8) and 16#ff#)) and 16#0000ff00#) xor
          (Td4(byte(            t2     and 16#ff#)) and 16#000000ff#) xor
          rk(4*Nr+1);

    s2 := (Td4(byte(Shift_Right(t2,24)           )) and 16#ff000000#) xor
          (Td4(byte(Shift_Right(t1,16) and 16#ff#)) and 16#00ff0000#) xor
          (Td4(byte(Shift_Right(t0, 8) and 16#ff#)) and 16#0000ff00#) xor
          (Td4(byte(            t3     and 16#ff#)) and 16#000000ff#) xor
          rk(4*Nr+2);

    s3 := (Td4(byte(Shift_Right(t3,24)           )) and 16#ff000000#) xor
          (Td4(byte(Shift_Right(t2,16) and 16#ff#)) and 16#00ff0000#) xor
          (Td4(byte(Shift_Right(t1, 8) and 16#ff#)) and 16#0000ff00#) xor
          (Td4(byte(            t0     and 16#ff#)) and 16#000000ff#) xor
          rk(4*Nr+3);

    -- map cipher state to byte array block:
    pt := block'(others => 0);
    SplitWord(s0, b0, b1, b2, b3);
    pt( 0) := b0; pt( 1) := b1; pt( 2) := b2; pt( 3) := b3;
    SplitWord(s1, b0, b1, b2, b3);
    pt( 4) := b0; pt( 5) := b1; pt( 6) := b2; pt( 7) := b3;
    SplitWord(s2, b0, b1, b2, b3);
    pt( 8) := b0; pt( 9) := b1; pt(10) := b2; pt(11) := b3;
    SplitWord(s3, b0, b1, b2, b3);
    pt(12) := b0; pt(13) := b1; pt(14) := b2; pt(15) := b3;
  end aesDecrypt;
end AES;
